from PyQt5.QtWidgets import QMainWindow, QMessageBox, QTextEdit
from PyQt5.QtNetwork import QUdpSocket, QHostAddress, QAbstractSocket
from PyQt5.QtCore import QDataStream, QIODevice, QByteArray, pyqtSlot, QThread, pyqtSignal
from mainwindow import Ui_MainWindow
import struct
import pyaudio
from unsloth import FastLanguageModel
import torch
import json
import websocket
import hashlib
import base64
import hmac
import json
from urllib.parse import urlencode
import time
import ssl
from wsgiref.handlers import format_date_time
from datetime import datetime
from time import mktime
import _thread as thread
import pyaudio


class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.times = 0
        self.udpSocket = QUdpSocket(self)
        self.udpSocket.readyRead.connect(self.readMsg)
        self.udpSocket.disconnected.connect(self.on_internet_disconnect_clicked)

        self.host = ""
        self.port = 0
        self.local_ip = QHostAddress()
        self.local_port = 0
        self.isConnected = False
        self.param_mode = 0
        self.stop = False
        self.b = 0

        self.x_target = 0
        self.y_target = 0
        self.z_target = 0
        self.pitch_target = 0
        self.yaw_target = 0
        self.roll_target = 0

        self.x_current = 0
        self.y_current = 0
        self.z_current = 0
        self.pitch_current = 0
        self.yaw_current = 0
        self.roll_current = 0

        self.l1 = 0
        self.l2 = 0
        self.l3 = 0
        self.l4 = 0
        self.l5 = 0
        self.l6 = 0

        self.ui.input_x.valueChanged.connect(
            lambda v: self.ui.input_x_slider.setValue(int(v)) if self.ui.input_x_slider.value() != v else None)
        self.ui.input_x_slider.valueChanged.connect(
            lambda v: self.ui.input_x.setValue(float(v)) if self.ui.input_x.value() != v else None)
        self.ui.input_y.valueChanged.connect(
            lambda v: self.ui.input_y_slider.setValue(int(v)) if self.ui.input_y_slider.value() != v else None)
        self.ui.input_y_slider.valueChanged.connect(
            lambda v: self.ui.input_y.setValue(float(v)) if self.ui.input_y.value() != v else None)
        self.ui.input_z.valueChanged.connect(
            lambda v: self.ui.input_z_slider.setValue(int(v)) if self.ui.input_z_slider.value() != v else None)
        self.ui.input_z_slider.valueChanged.connect(
            lambda v: self.ui.input_z.setValue(float(v)) if self.ui.input_z.value() != v else None)
        self.ui.input_pitch.valueChanged.connect(
            lambda v: self.ui.input_pitch_slider.setValue(int(v)) if self.ui.input_pitch_slider.value() != v else None)
        self.ui.input_pitch_slider.valueChanged.connect(
            lambda v: self.ui.input_pitch.setValue(float(v)) if self.ui.input_pitch.value() != v else None)
        self.ui.input_yaw.valueChanged.connect(
            lambda v: self.ui.input_yaw_slider.setValue(int(v)) if self.ui.input_yaw_slider.value() != v else None)
        self.ui.input_yaw_slider.valueChanged.connect(
            lambda v: self.ui.input_yaw.setValue(float(v)) if self.ui.input_yaw.value() != v else None)
        self.ui.input_roll.valueChanged.connect(
            lambda v: self.ui.input_roll_slider.setValue(int(v)) if self.ui.input_roll_slider.value() != v else None)
        self.ui.input_roll_slider.valueChanged.connect(
            lambda v: self.ui.input_roll.setValue(float(v)) if self.ui.input_roll.value() != v else None)
        self.ui.max_v.valueChanged.connect(
            lambda v: self.ui.max_v_slider.setValue(int(v)) if self.ui.max_v_slider.value() != v else None)
        self.ui.max_v_slider.valueChanged.connect(
            lambda v: self.ui.max_v.setValue(float(v)) if self.ui.max_v.value() != v else None)
        self.ui.max_omega.valueChanged.connect(
            lambda v: self.ui.max_omega_slider.setValue(int(v)) if self.ui.max_omega_slider.value() != v else None)
        self.ui.max_omega_slider.valueChanged.connect(
            lambda v: self.ui.max_omega.setValue(float(v)) if self.ui.max_omega.value() != v else None)
        self.ui.max_a.valueChanged.connect(
            lambda v: self.ui.max_a_slider.setValue(int(v)) if self.ui.max_a_slider.value() != v else None)
        self.ui.max_a_slider.valueChanged.connect(
            lambda v: self.ui.max_a.setValue(float(v)) if self.ui.max_a.value() != v else None)
        self.ui.max_beta.valueChanged.connect(
            lambda v: self.ui.max_beta_slider.setValue(int(v)) if self.ui.max_beta_slider.value() != v else None)
        self.ui.max_beta_slider.valueChanged.connect(
            lambda v: self.ui.max_beta.setValue(float(v)) if self.ui.max_beta.value() != v else None)

        self.model = None
        self.tokenizer = None
        self.load_model_thread = ModelLoaderThread()
        self.load_model_thread.model_loaded.connect(self.on_model_loaded)
        self.load_model_thread.start()

        self.ui.command_zero.setText("模型加载中")
        self.ui.status.display(0)
        # self.ui.voice_send.clicked.connect(self.run_inference())

    def on_model_loaded(self, model, tokenizer):
        self.model = model
        self.tokenizer = tokenizer
        print("模型加载完成")
        self.ui.command_zero.setText("模型加载完成")
        self.ui.status.display(1)

    @pyqtSlot()
    def on_voice_send_clicked(self):
        question = self.ui.ai_input.toPlainText().strip()
        print(question)
        if not question:
            QMessageBox.warning(self, "警告", "请输入问题!")
            return
        if self.model is None or self.tokenizer is None:
            QMessageBox.warning(self, "警告", "模型尚未加载完成，请稍后重试!")
            return
        self.ui.ai_input.clear()
        self.ui.ai_text.append(question)
        self.inference_thread = InferenceThread(self.model, self.tokenizer, question)
        self.inference_thread.result_ready.connect(self.display_result)
        self.inference_thread.start()

    def display_result(self, result):
        # self.ui.ai_text.setText(result)
        # 上位机处理
        try:
            # 新增：清理结束符并解析JSON
            result = result.split("<|im_end|>")[0].strip()
            data = json.loads(result)

            # 提取数据到变量
            x = data.get("x", 0)
            y = data.get("y", 0)
            z = data.get("z", 0)
            rot_x = data.get("rot_x", 0)
            rot_y = data.get("rot_y", 0)
            rot_z = data.get("rot_z", 0)

            self.ui.input_x.setValue(x)
            self.ui.input_y.setValue(y)
            self.ui.input_z.setValue(z)
            self.ui.input_pitch.setValue(rot_x)
            self.ui.input_yaw.setValue(rot_y)
            self.ui.input_roll.setValue(rot_z)


        except json.JSONDecodeError as e:
            QMessageBox.warning(self, "解析错误", f"无效的JSON格式: {str(e)}")
        except Exception as e:
            QMessageBox.warning(self, "运行错误", f"处理数据时发生异常: {str(e)}")

    def on_internet_connect_clicked(self):
        self.host = self.ui.ip_server.text().strip()
        self.port = int(self.ui.ip_port.text())
        self.local_port = int(self.ui.ip_port_2.text())
        if self.udpSocket.state() == QAbstractSocket.UnconnectedState:
            self.udpSocket.bind(QHostAddress.Any, self.local_port)
            self.ui.internet_connect.setText("重连")
            '''debug'''
            print('已连接')
        self.local_ip = QHostAddress(self.ui.ip_local.text().strip())
        if not self.host or self.port == 0:
            QMessageBox.information(None, "警告", "请输入完整信息", QMessageBox.Yes)
            return

    def on_internet_disconnect_clicked(self):
        self.udpSocket.abort()
        self.ui.internet_connect.setText("连接")

    def readMsg(self):
        while self.udpSocket.hasPendingDatagrams():
            datagram, host, port = self.udpSocket.readDatagram(self.udpSocket.pendingDatagramSize())
            receive_from_plc = self.deserializeFrame(datagram)

            if receive_from_plc['status'] == 1:
                self.ui.command_zero.setText("初始化中")
            elif receive_from_plc['status'] == 2:
                self.ui.command_zero.setText("初始化完成")
                self.isConnected = True
            elif receive_from_plc['status'] == 3:
                self.ui.command_zero.setText("回零中")
            elif receive_from_plc['status'] == 4:
                self.ui.command_zero.setText("已回零")
            elif receive_from_plc['status'] == 5:
                self.ui.command_zero.setText("定位中")
            elif receive_from_plc['status'] == 6:
                self.ui.command_zero.setText("插入中")
            elif receive_from_plc['status'] == 7:
                self.ui.command_zero.setText("安装到位")
            elif receive_from_plc['status'] == 8:
                self.ui.command_zero.setText("推出中")
            elif receive_from_plc['status'] == 9:
                self.ui.command_zero.setText("急停")
            elif receive_from_plc['status'] == 10:
                self.ui.command_zero.setText("故障")
            elif receive_from_plc['status'] == 11:
                self.ui.command_zero.setText("角度软极限")
            elif receive_from_plc['status'] == 12:
                self.ui.command_zero.setText("杆长软限位")
            elif receive_from_plc['status'] == 13:
                self.ui.command_zero.setText("停止运动")
            elif receive_from_plc['status'] == 14:
                self.ui.command_zero.setText("硬限位")

            self.ui.status.display(int(receive_from_plc['status']))
            self.ui.x.display(receive_from_plc['x'])
            self.ui.y.display(receive_from_plc['y'])
            self.ui.z.display(receive_from_plc['z'])
            self.ui.roll.display(receive_from_plc['roll'])
            self.ui.pitch.display(receive_from_plc['pitch'])
            self.ui.yaw.display(receive_from_plc['yaw'])

            self.ui.l1.display(receive_from_plc['l1'])
            self.ui.l2.display(receive_from_plc['l2'])
            self.ui.l3.display(receive_from_plc['l3'])
            self.ui.l4.display(receive_from_plc['l4'])
            self.ui.l5.display(receive_from_plc['l5'])
            self.ui.l6.display(receive_from_plc['l6'])

            t1 = receive_from_plc['torque'][0] / 16384 * 2.016 / 0.64 * 100
            t2 = receive_from_plc['torque'][1] / 16384 * 2.016 / 0.64 * 100
            t3 = receive_from_plc['torque'][2] / 16384 * 2.016 / 0.64 * 100
            t4 = receive_from_plc['torque'][3] / 16384 * 2.016 / 0.64 * 100
            t5 = receive_from_plc['torque'][4] / 16384 * 2.016 / 0.64 * 100
            t6 = receive_from_plc['torque'][5] / 16384 * 2.016 / 0.64 * 100

            ave_t = (t1 + t2 + t3 + t4 + t5 + t6) / 6
            self.b = 0.99 * self.b + 0.01 * ave_t

            self.ui.torque1.display(t1)
            self.ui.torque2.display(t2)
            self.ui.torque3.display(t3)
            self.ui.torque4.display(t4)
            self.ui.torque5.display(t5)
            self.ui.torque6.display(t6)
            self.ui.torque_all.display(self.b)

    def on_motor_on_clicked(self):
        """
        不需要了，还存在问题
        """
        if self.ui.motor_on.text() == "启动电机":
            send_to_plc = {
                'command': 1
            }
            self.ui.command_control.setText("使能")
            self.ui.control.display(int(send_to_plc['command']))
            self.ui.motor_on.setText("关闭电机")
            self.ui.motor1.setText("已启动")
            self.ui.motor2.setText("已启动")
            self.ui.motor3.setText("已启动")
            self.ui.motor4.setText("已启动")
            self.ui.motor5.setText("已启动")
            self.ui.motor6.setText("已启动")
        elif self.ui.motor_on.text() == "关闭电机":
            self.ui.motor_on.setText("启动电机")
            self.ui.motor1.setText("已关闭")
            self.ui.motor2.setText("已关闭")
            self.ui.motor3.setText("已关闭")
            self.ui.motor4.setText("已关闭")
            self.ui.motor5.setText("已关闭")
            self.ui.motor6.setText("已关闭")

    @pyqtSlot()  # 每个信号只调用一次
    def on_init_clicked(self):
        send = {
            'command': 2
        }
        self.ui.command_control.setText("回零")
        print('回零')
        self.stop = False
        self.ui.control.display(int(send['command']))
        data = self.serializeFrame(send)
        self.udpSocket.writeDatagram(data, QHostAddress(self.host), self.port)

    @pyqtSlot()
    def on_input_clicked(self):
        if self.stop:
            QMessageBox.information(None, "警告", "请点击“继续运动”按钮！", QMessageBox.Yes)
            return

        mode = ""
        param_coord = 0

        if self.ui.mode_absolute.isChecked():
            mode = self.ui.mode_absolute.text() + "\n"
            self.param_mode = 1
        elif self.ui.mode_relative.isChecked():
            mode = self.ui.mode_relative.text() + "\n"
            self.param_mode = 2
        elif self.ui.mode_force_and_position.isChecked():
            mode = self.ui.mode_force_and_position.text() + "\n"
            self.param_mode = 3

        if not (
                self.ui.mode_absolute.isChecked() or self.ui.mode_relative.isChecked() or self.ui.mode_force_and_position.isChecked()):
            QMessageBox.information(None, "警告", "请选择定位模式", QMessageBox.Yes)
            return

        if self.ui.base_coordinate.isChecked():
            mode += self.ui.base_coordinate.text()
            param_coord = 1
        elif self.ui.platform_coordinate.isChecked():
            mode += self.ui.platform_coordinate.text()
            param_coord = 2

        if not (self.ui.base_coordinate.isChecked() or self.ui.platform_coordinate.isChecked()):
            QMessageBox.information(None, "警告", "请选择坐标模式", QMessageBox.Yes)
            return

        if not (self.ui.input_x.text() and self.ui.input_y.text() and self.ui.input_z.text() and
                self.ui.input_roll.text() and self.ui.input_pitch.text() and self.ui.input_yaw.text()):
            QMessageBox.information(None, "警告", "请输入完整数据", QMessageBox.Yes)
            return

        send_to_plc = {
            'command': 3,
            'subCommand': self.param_mode,
            'subCommand2': param_coord
        }

        if self.param_mode == 1:
            roll_in = float(self.ui.input_roll.text())
            pitch_in = float(self.ui.input_pitch.text())
        elif self.param_mode == 2:
            roll_in = float(self.ui.input_pitch.text())
            pitch_in = float(self.ui.input_roll.text())

        self.ui.command_control.setText("定位中")
        self.ui.control.display(int(send_to_plc['command']))
        self.ui.subcommand.display(self.param_mode)
        self.ui.subcommand2.display(param_coord)

        x_in = float(self.ui.input_x.text())
        y_in = float(self.ui.input_y.text())
        z_in = float(self.ui.input_z.text())
        roll_in = float(self.ui.input_roll.text())
        pitch_in = float(self.ui.input_pitch.text())
        yaw_in = float(self.ui.input_yaw.text())

        send_to_plc['x'] = x_in
        send_to_plc['y'] = y_in
        send_to_plc['z'] = z_in
        send_to_plc['roll'] = roll_in
        send_to_plc['pitch'] = pitch_in
        send_to_plc['yaw'] = yaw_in

        self.ui.mode_current.setText(mode)
        data = self.serializeFrame(send_to_plc)
        self.udpSocket.writeDatagram(data, QHostAddress(self.host), self.port)

    def on_stop_clicked(self):
        send = {
            'command': 8
        }
        self.stop = True
        self.ui.control.display(int(send['command']))
        self.ui.command_control.setText("停止运动")
        data = self.serializeFrame(send)
        self.udpSocket.writeDatagram(data, QHostAddress(self.host), self.port)

    def on_continue_2_clicked(self):
        send = {
            'command': 9
        }
        self.stop = False
        self.ui.command_control.setText("继续运动")
        self.ui.control.display(int(send['command']))
        data = self.serializeFrame(send)

        self.udpSocket.writeDatagram(data, QHostAddress(self.host), self.port)

    def on_self_check_clicked(self):
        send = {
            'command': 8
        }
        self.ui.is_checked.setText("自检中")
        data = self.serializeFrame(send)
        self.udpSocket.writeDatagram(data, QHostAddress(self.host), self.port)
        self.ui.is_checked.setText("已自检")

    def on_input_max_clicked(self):
        if not (self.ui.max_v.text() and self.ui.max_a.text() and self.ui.max_omega.text() and self.ui.max_beta.text()):
            QMessageBox.information(None, "警告", "请输入完整数据", QMessageBox.Yes)
            return

    def serializeFrame(self, frame):
        data = QByteArray()
        stream = QDataStream(data, QIODevice.WriteOnly)
        stream.setByteOrder(QDataStream.BigEndian)

        stream.writeUInt32(0x55555555)
        stream.writeUInt32(frame.get('count', 0))
        stream.writeUInt32(frame['command'])
        stream.writeUInt32(frame.get('subCommand', 0))
        stream.writeUInt32(frame.get('subCommand2', 0))

        # 修改所有浮点数字段使用struct打包
        stream.writeRawData(struct.pack('!f', frame.get('x', 0)))  # !表示网络字节序（大端）
        stream.writeRawData(struct.pack('!f', frame.get('y', 0)))
        stream.writeRawData(struct.pack('!f', frame.get('z', 0)))
        stream.writeRawData(struct.pack('!f', frame.get('roll', 0)))
        stream.writeRawData(struct.pack('!f', frame.get('yaw', 0)))
        stream.writeRawData(struct.pack('!f', frame.get('pitch', 0)))

        stream.writeUInt32(0xaaaaaaaa)
        print('send:')
        print(data.toHex())
        return bytes(data.data())

    def deserializeFrame(self, data):
        byte_array = QByteArray(data)
        stream = QDataStream(byte_array, QIODevice.ReadOnly)
        stream.setByteOrder(QDataStream.BigEndian)
        frame = {}

        def read_float():
            return struct.unpack('!f', stream.readRawData(4))[0]

        frame['head'] = stream.readUInt32()
        frame['count'] = stream.readUInt32()
        frame['status'] = stream.readUInt32()
        frame['subStatus1'] = stream.readUInt32()
        frame['subStatus2'] = stream.readUInt32()
        frame['subStatus3'] = stream.readUInt32()
        frame['subStatus4'] = stream.readUInt32()

        # 修改所有浮点数字段读取方式
        frame['x'] = read_float()
        frame['y'] = read_float()
        frame['z'] = read_float()
        frame['roll'] = read_float()
        frame['yaw'] = read_float()
        frame['pitch'] = read_float()

        # 后续所有浮点字段使用相同方式读取
        frame['l1'] = read_float()
        frame['l2'] = read_float()
        frame['l3'] = read_float()
        frame['l4'] = read_float()
        frame['l5'] = read_float()
        frame['l6'] = read_float()
        frame['sensor1'] = read_float()
        frame['sensor2'] = read_float()
        frame['sensor3'] = read_float()

        # 修改torque数组读取方式
        frame['torque'] = [read_float() for _ in range(6)]

        frame['tail'] = stream.readUInt32()
        print('receive:')
        print(byte_array.toHex())
        print(frame)
        return frame

    def on_clear_clicked(self):
        self.ui.input_x.setValue(0)
        self.ui.input_y.setValue(0)
        self.ui.input_z.setValue(0)
        self.ui.input_roll.setValue(0)
        self.ui.input_pitch.setValue(0)
        self.ui.input_yaw.setValue(0)

    def on_mode_absolute_toggled(self, checked):
        if checked:
            self.ui.position.setText("位置(mm)")
            self.ui.attitude.setText("姿态(°)")
            self.ui.pitch_box.setTitle("俯仰pitch")
            self.ui.yaw_box.setTitle("偏航yaw")
            self.ui.roll_box.setTitle("滚转roll")
            self.param_mode = 1

    def on_mode_relative_toggled(self, checked):
        if checked:
            self.ui.position.setText("位置增量(mm)")
            self.ui.attitude.setText("姿态增量(°)")
            self.ui.pitch_box.setTitle("绕x轴旋转")
            self.ui.yaw_box.setTitle("绕y轴旋转")
            self.ui.roll_box.setTitle("绕z轴旋转")
            self.param_mode = 2

    def on_voice_input_clicked(self):
        return


"""
大模型加载线程。
"""


class ModelLoaderThread(QThread):
    model_loaded = pyqtSignal(object, object)  # Signal to pass model and tokenizer

    def run(self):
        max_seq_length = 1024
        dtype = None
        load_in_4bit = True
        model, tokenizer = FastLanguageModel.from_pretrained(
            model_name="D:\\models\\merged_model",
            max_seq_length=max_seq_length,
            dtype=dtype,
            load_in_4bit=load_in_4bit,
        )
        FastLanguageModel.for_inference(model)
        self.model_loaded.emit(model, tokenizer)


class InferenceThread(QThread):
    result_ready = pyqtSignal(str)  # Signal to pass the model's response

    def __init__(self, model, tokenizer, question):
        super().__init__()
        self.model = model
        self.tokenizer = tokenizer
        self.question = question

    def run(self):
        prompt_style = """以下是描述任务的指令。请编写一个恰当完成请求的响应。
                        写一个适当完成请求的响应。
                        
                        ### Instruction:
                        你是一个自然语言处理助手，请从以下问题中提取与运动相关的数值，按照如下 JSON 格式输出：
                        
                        [
                          "x": x轴方向的位移（单位：毫米，未提及为0）,
                          "y": y轴方向的位移（单位：毫米，未提及为0）,
                          "z": z轴方向的位移（单位：毫米，未提及为0）,
                          "rot_x": 绕x轴的旋转角度（单位：度，未提及为0）,
                          "rot_y": 绕y轴的旋转角度（单位：度，未提及为0）,
                          "rot_z": 绕z轴的旋转角度（单位：度，未提及为0）
                        ]
                        
                        ️ **注意事项**：
                        1. 只输出上述格式的 JSON 数据，不能输出任何其他文字。
                        2. 未提及的方向/角度，值设为 `0`。
                        3. 请确保数值为数字（浮点或整数），不要添加单位。
                        4. 请不要添加解释、注释或其他说明。
                        
                        ### Question:
                        {}
                        
                        ### Response:
                        {}"""
        inputs = self.tokenizer([prompt_style.format(self.question, "")], return_tensors="pt").to("cuda")
        outputs = self.model.generate(
            input_ids=inputs.input_ids,
            attention_mask=inputs.attention_mask,
            max_new_tokens=1200,
            use_cache=True,
            pad_token_id=self.tokenizer.pad_token_id,
            temperature=0.2,
            top_p=0.5,
        )
        response = self.tokenizer.batch_decode(outputs)
        result = response[0].split("### Response:")[1].strip()
        self.result_ready.emit(result)


class VoiceRecognizer(QThread):
    result_ready = pyqtSignal(str)

    def __init__(self):
        super().__init__()
        # 初始化讯飞接口参数（需要填写实际API参数）
        self.APPID = 'your_appid'
        self.APIKey = 'your_apikey'
        self.APISecret = 'your_apisecret'
        self.is_recording = False
        self.stream = None

    def create_url(self):
        # 生成websocket连接URL的逻辑（同原voice.py）
        url = 'wss://ws-api.xfyun.cn/v2/iat'
        now = datetime.now()
        date = format_date_time(mktime(now.timetuple()))
        signature_origin = "host: ws-api.xfyun.cn\ndate: {}\nGET /v2/iat HTTP/1.1".format(date)
        signature_sha = hmac.new(self.APISecret.encode(), signature_origin.encode(), hashlib.sha256).digest()
        signature_sha = base64.b64encode(signature_sha).decode()
        authorization = "api_key=\"%s\", algorithm=\"hmac-sha256\", headers=\"host date request-line\", signature=\"%s\"" % (
        self.APIKey, signature_sha)
        return url + '?' + urlencode({"authorization": base64.b64encode(authorization.encode()).decode(), "date": date,
                                      "host": "ws-api.xfyun.cn"})

    def run(self):
        # 录音和websocket通信主逻辑
        self.is_recording = True
        ws = websocket.WebSocketApp(self.create_url(),
                                    on_message=self.on_message,
                                    on_error=self.on_error)
        ws.on_open = lambda _: self.on_open(ws)
        ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})

    def stop_recording(self):
        # 停止录音
        self.is_recording = False
        if self.stream:
            self.stream.stop_stream()
            self.stream.close()

    # 以下回调函数保持原voice.py逻辑
    def on_open(self, ws):
        # 录音和发送逻辑（同原voice.py）
        p = pyaudio.PyAudio()
        self.stream = p.open(format=pyaudio.paInt16,
                             channels=1,
                             rate=16000,
                             input=True,
                             frames_per_buffer=520)
        # 音频发送逻辑（同原voice.py）...

    def on_message(self, ws, message):
        # 结果处理逻辑（同原voice.py）
        try:
            data = json.loads(message)["data"]["result"]["ws"]
            result = "".join([w["w"] for i in data for w in i["cw"]])
            if result.strip() not in ['。', '.。', ' .。', ' 。']:
                self.result_ready.emit(result)
        except Exception as e:
            print("Error:", e)

